﻿using ScintillaNET;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Teal.Compiler;

namespace Teal.Compiler.Tester {
    public partial class MainWindow : Form {

        #region 工具成员

        Scintilla codeEditor;

        public MainWindow() {
            InitializeComponent();

            try {
                codeEditor = new Scintilla();
                codeEditor.ConfigurationManager.Language = "tea";
                codeEditor.Dock = DockStyle.Fill;
                codeEditor.Font = rtbSourceCode.Font;
                codeEditor.Text = rtbSourceCode.Text;
                codeEditor.LineWrapping.Mode = LineWrappingMode.Word;
                codeEditor.Margins[0].Width = 30;
                codeEditor.Selection.BackColorUnfocused = codeEditor.Selection.BackColor;
                codeEditor.Styles[125].ForeColor = Color.Red;
                codeEditor.Styles[125].Underline = true;
                codeEditor.Styles[126].ForeColor = Color.Orange;
                codeEditor.Styles[126].Underline = true;

                splitContainer1.Panel1.Controls.Clear();
                splitContainer1.Panel1.Controls.Add(codeEditor);
            } catch { }

            Compiler.reportor = new MyErrorReportor(this);
            Compiler.options.outputPath = Path.GetFullPath("hello.exe");
            Compiler.options.tempPath = Path.GetFullPath("temp");

        }

        class MyErrorReportor : IReporter {

            MainWindow _window;

            public MyErrorReportor(MainWindow window) {
                _window = window;
            }

            void addLine(string number, string message, Location startLocation, Location endLocation) {
                _window.splitContainer2.Panel1Collapsed = false;
                _window.dataGridView1.Show();
                _window.dataGridView1.Rows[_window.dataGridView1.Rows.Add(number, message, startLocation.row, startLocation.column)].Tag = new Location[] { startLocation, endLocation };
            }

            /// <summary>
            /// 输出一个错误信息。
            /// </summary>
            /// <param name="errorNumber">错误序号。</param>
            /// <param name="message">错误信息。</param>
            /// <param name="startLocation">错误在源码内的起始位置。</param>
            /// <param name="endLocation">错误在源码内的结束位置。</param>
            public void error(ErrorCode errorNumber, string message, Location startLocation, Location endLocation) {

                addLine(String.Format("TE{0:0000}", (int)errorNumber), message, startLocation, endLocation);

                if (startLocation.row == 0 || endLocation.row == 0) {
                    return;
                }

                if (_window.codeEditor != null) {


                    using (var rng = _window.codeEditor.GetRange(_window.LocationToPosition(startLocation), _window.LocationToPosition(endLocation))) {
                        rng.SetStyle(125);
                        rng.Select();
                    }

                    return;
                }


                int start = _window.rtbSourceCode.GetFirstCharIndexFromLine((int)startLocation.row - 1) + (int)startLocation.column - 1;
                int end = _window.rtbSourceCode.GetFirstCharIndexFromLine((int)endLocation.row - 1) + (int)endLocation.column - 1;

                if (_window.rtbSourceCode.GetLineFromCharIndex(end) == endLocation.row) {
                    _window.rtbSourceCode.Select(start, 0);
                    _window.rtbSourceCode.SelectedText = "_";
                    _window.rtbSourceCode.SelectionColor = Color.Red;
                }

                _window.rtbSourceCode.Select(start, end - start);
                _window.rtbSourceCode.SelectionColor = Color.Red;
                _window.rtbSourceCode.SelectionLength = 0;
            }

            /// <summary>
            /// 输出一个警告信息。
            /// </summary>
            /// <param name="warningNumber">警告序号。</param>
            /// <param name="message">警告信息。</param>
            /// <param name="startLocation">警告在源码内的起始位置。</param>
            /// <param name="endLocation">警告在源码内的结束位置。</param>
            public void warning(ErrorCode warningNumber, string message, Location startLocation, Location endLocation) {

                addLine(String.Format("TW{0:0000}", (int)warningNumber), message, startLocation, endLocation);

                if (startLocation.row == 0 || endLocation.row == 0) {
                    return;
                }

                if (_window.codeEditor != null) {


                    using (var rng = _window.codeEditor.GetRange(_window.LocationToPosition(startLocation), _window.LocationToPosition(endLocation))) {
                        rng.SetStyle(126);
                        rng.Select();
                    }


                    return;
                }


                int start = _window.rtbSourceCode.GetFirstCharIndexFromLine((int)startLocation.row - 1) + (int)startLocation.column - 1;
                int end = _window.rtbSourceCode.GetFirstCharIndexFromLine((int)endLocation.row - 1) + (int)endLocation.column - 1;

                if (_window.rtbSourceCode.GetLineFromCharIndex(end) == (int)endLocation.row) {
                    _window.rtbSourceCode.Select(start, 0);
                    _window.rtbSourceCode.SelectedText = "_";
                    _window.rtbSourceCode.SelectionColor = Color.Orange;
                }

                _window.rtbSourceCode.Select(start, end - start);
                _window.rtbSourceCode.SelectionColor = Color.Orange;
                _window.rtbSourceCode.SelectionLength = 0;
            }

            /// <summary>
            /// 输出一个信息。
            /// </summary>
            /// <param name="message">要输出的信息。</param>
            /// <param name="startLocation">产生此信息的起始位置。</param>
            /// <param name="endLocation">产生此信息的结束位置。</param>
            public void echo(string message, Location startLocation, Location endLocation) {
                addLine("message", message, startLocation, endLocation);
            }

            public void clear() {

                _window.dataGridView1.Hide();
                _window.dataGridView1.Rows.Clear();

                _window.splitContainer2.Panel1Collapsed = true;

                if (_window.codeEditor != null) {
                    return;
                }

                int oldSelectionStart = _window.rtbSourceCode.SelectionStart;
                int oldSelectionLength = _window.rtbSourceCode.SelectionLength;

                _window.rtbSourceCode.SelectAll();
                _window.rtbSourceCode.SelectionColor = Color.Black;

                _window.rtbSourceCode.SelectionStart = oldSelectionStart;
                _window.rtbSourceCode.SelectionLength = oldSelectionLength;
            }

        }

        public string SourceCode {
            get {
                if (codeEditor != null)
                    return codeEditor.Text;

                return rtbSourceCode.Text;
            }
            set {
                if (codeEditor != null)
                    codeEditor.Text = value;
                else
                    rtbSourceCode.Text = value;
            }
        }

        int LocationToPosition(Location value) {
            return codeEditor.Lines[value.row - 1].StartPosition + value.column - 1;
        }

        void Select(Location start, Location end) {
            if (start.row == 0 || end.row == 0) {
                return;
            }

            if (codeEditor != null) {
                codeEditor.Selection.Start = LocationToPosition(start);
                codeEditor.Selection.End = LocationToPosition(end);

                return;
            }


            int startPos = rtbSourceCode.GetFirstCharIndexFromLine((int)start.row - 1) + (int)start.column - 1;
            int endPos = rtbSourceCode.GetFirstCharIndexFromLine((int)end.row - 1) + (int)end.column - 1;
            rtbSourceCode.Select(startPos, endPos - startPos);
        }

        void ShowControl(Control control) {
            control.Dock = DockStyle.Fill;
            splitContainer2.Panel2.Controls.Clear();
            splitContainer2.Panel2.Controls.Add(control);
        }

        void Reset() {
            if (codeEditor != null) {
                rtbSourceCode.Text = Teal.Compiler.Tester.Properties.Settings.Default.RichTextBoxText = codeEditor.Text;
            }

            Teal.Compiler.Tester.Properties.Settings.Default.Save();
            Compiler.reset();
            ((MyErrorReportor) Compiler.reportor).clear();
        }

        private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e) {
            var dat = (Location[])dataGridView1.Rows[e.RowIndex].Tag;
            Select(dat[0], dat[1]);
        }

        private void dataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e) {
            System.Windows.Forms.Clipboard.SetText(dataGridView1.Rows[e.RowIndex].Cells[1].Value.ToString());
        }

        #endregion

        #region 常用代码

        private void helloWorldToolStripMenuItem_Click(object sender, EventArgs e) {
            SourceCode = @"import System;

void main(){
    Console.writeLine(""hello world"");
    Console.pause();
}";
        }

        private void mainToolStripMenuItem_Click(object sender, EventArgs e) {
            SourceCode = @"import System;

void main(){
    
}";
        }

        #endregion

        #region 词法

        private void lexerToolStripMenuItem_Click(object sender, EventArgs e) {
            Reset();

            ListBox listBox = new ListBox();
            listBox.SelectedIndexChanged += listBox_SelectedIndexChanged;
            listBox.Click += listBox_SelectedIndexChanged;

            Teal.Compiler.Lexer lexer = new Teal.Compiler.Lexer(new StringReader(SourceCode));
            while (lexer.read().type != TokenType.eof) {
                listBox.Items.Add(new TokenListItem(lexer.current));
            }

            ShowControl(listBox);

        }

        sealed class TokenListItem {
            public string Value;
            public Location StartLocation;
            public Location EndLocation;
            public TokenListItem(Token token) {
                Value = String.Format("{0,-20}{1}", "[" + token.type.getName() + "]", token.ToString());
                StartLocation = token.startLocation;
                EndLocation = token.endLocation;
            }
            public override string ToString() {
                return Value;
            }
        }

        void listBox_SelectedIndexChanged(object sender, EventArgs e) {
            TokenListItem t = (TokenListItem)((ListBox)sender).SelectedItem;
            if (t != null)
                Select(t.StartLocation, t.EndLocation);
        }

        #endregion

        #region 语法

        private void parserToolStripMenuItem_Click(object sender, EventArgs e) {
            Reset();
            var parser = new Parser();
            var module = parser.parseString(SourceCode);

            var treeView = new TreeView();
            treeView.AfterSelect += treeView_Syntax_AfterSelect;
            DrawAst(treeView, module);
            ShowControl(treeView);
        }

        #endregion

        #region 语义

        private void resolveToolStripMenuItem_Click(object sender, EventArgs e) {
            Reset();
            var parser = new Parser();

            var module = Compiler.currentModule;
            var unit =parser.parseString(SourceCode);

            module.members = unit;

            // 出现了语法错误，不继续分析。
            if (Compiler.errorCount == 0) {
                module.resolve();
            }

            var treeView = new TreeView();
            treeView.AfterSelect += treeView_Syntax_AfterSelect;
            DrawAst(treeView, unit);
            ShowControl(treeView);

        }

        #endregion

        #region 生成

        private void generateToolStripMenuItem_Click(object sender, EventArgs e) {

            Reset();
            var parser = new Parser();
            var sourceUnit = parser.parseString(SourceCode);
            Compiler.currentModule.members = sourceUnit;
            if (Compiler.errorCount > 0)
                return;
            Compiler.currentPackage.resolve();
            if (Compiler.errorCount > 0)
                return;
            Compiler.currentPackage.emit();
            Compiler.currentPackage.save();

            var treeView = new TreeView();
            treeView.AfterSelect += treeView_Syntax_AfterSelect;
            DrawAst(treeView, sourceUnit);
            ShowControl(treeView);

        }

        #endregion

        #region 执行

        private void executeToolStripMenuItem_Click(object sender, EventArgs e) {
            Compiler.runIfSuccess = true;
            generateToolStripMenuItem_Click(sender, e);
            Compiler.runIfSuccess = false;
        }

        #endregion

        #region 绘制语法树

        void treeView_Syntax_AfterSelect(object sender, TreeViewEventArgs e) {
            if (e.Node.Tag is Node) {
                Node node = (Node)e.Node.Tag;
                Select(node.startLocation, node.endLocation);
            }
        }

        private static void DrawAst(TreeView treeView, SourceUnit module) {
            var drawed = new List<Node>();
            var node = treeView.Nodes.Add("<语法树>");
            DrawAst(node, null, module, drawed);
            DrawAstAll(node, drawed);
        }

        private static void DrawAst(TreeNode parentNode, string key, object value, List<Node> drawed) {

            // 展开最初 3 级。
            if (parentNode.Level <= 3) {
                parentNode.Expand();
            }

            TreeNode treeNode = parentNode.Nodes.Add(key ?? "");
            
            if (key != null) {
                treeNode.Text += " = ";
                treeNode.ForeColor = Color.Gray;
            }

            treeNode.Tag = value;

            if (value == null) {
                treeNode.Text += "null";
                return;
            }

            if (value is bool) {
                treeNode.Text += ((bool)value) ? "true" : "false";
                return;
            }

            if (value is string || value is Identifier || value is ValueType) {
                var v = value.ToString();
                if (v.Length == 0) {
                    treeNode.Text += "(empty)";
                } else {
                    treeNode.Text += value.ToString();
                }
                return;
            }

            if (value is Node) {

                System.Type type = value.GetType();
                string nodeToString;
                try {
                    nodeToString = value.ToString();
                } catch {
                    nodeToString = "<此节点有错误>";
                }

                treeNode.Text += String.Format("[{0}]  {1}", type.Name, nodeToString);
                treeNode.ToolTipText = nodeToString;
                treeNode.Tag = value;

                // 显示属性。

                Node v = ((Node)value);

                if (drawed.Contains(v)) {
                    return;
                }

                drawed.Add(v);

                if (value is MemberContainerDefinition) {
                    MemberContainerDefinition t = (MemberContainerDefinition)value;
                    for (var member = t.members; member != null; member = member.next) {
                        DrawAst(treeNode, null, member, drawed);
                    }
                }

                if (value is MethodDefinition) {
                    DrawAst(treeNode, "body", ((MethodDefinition)value).body, drawed);
                }

                return;
            }

            if (value is System.Collections.IEnumerable) {
                int i = 0;
                foreach (object item in (System.Collections.IEnumerable)value) {
                    i++;
                    DrawAst(treeNode, null, item, drawed);
                }

                if (i > 0) {
                    treeNode.ForeColor = Color.Black;
                    treeNode.Text += i + " member(s)";
                } else {
                    treeNode.Text += "(empty)";
                }
                return;
            }

            try {
                treeNode.Text += value.ToString();
            } catch {
            }

            if (value is MemberReference) {
                foreach (FieldInfo p in value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public)) {
                    string cc = "<错误>";
                    try {
                        var ccc = p.GetValue(value);
                        if (ccc == null) {
                            cc = "null";
                        } else {
                            cc = ccc.ToString();
                        }
                    } catch {

                    }
                    treeNode.Nodes.Add(p.Name + " = " + cc);
                }
                //foreach (PropertyInfo p in value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
                //    string cc = "<错误>";
                //    try {
                //        var ccc = p.GetValue(value);
                //        if (ccc == null) {
                //            cc = "null";
                //        } else {
                //            cc = ccc.ToString();
                //        }
                //    } catch {

                //    }
                //    treeNode.Nodes.Add(p.Name + " = " + cc);
                //}
            }

        }

        private static void DrawAstAll(TreeNode treeNode, List<Node> drawed) {
            foreach (TreeNode n in treeNode.Nodes) {
                DrawAstAll(n, drawed);
            }
            if (treeNode.Tag is Node) {
                var child = treeNode.Nodes.Insert(0, "属性");
                child.ForeColor = Color.Gray;
                foreach (FieldInfo p in treeNode.Tag.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public)) {
                    DrawAst(child, p.Name, p.GetValue(treeNode.Tag), drawed);
                }
                child.Collapse();
            }
        }

        #endregion

        private void toolStripMenuItem1_Click(object sender, EventArgs e) {
            //Reset();
            //var parser = new Parser();

            //var package = Compiler.currentPackage;
            //package.members = parser.parseString(SourceCode);

            //if (Compiler.errorCount > 0) {
            //    return;
            //}

            //package.resolve();

            //StringBuilder sb = new StringBuilder();
            //StringWriter sw = new StringWriter(sb);
            //CCodeGenContext context = new CCodeGenContext(sw);
            //package.emit(context);

            //var textBox = new TextBox();
            //textBox.Multiline = true;
            //textBox.Text = sb.ToString();

            //ShowControl(textBox);

        }

        private void toolStripMenuItem2_Click(object sender, EventArgs e) {
            Reset();
            var parser = new Parser();

            var package = Compiler.currentModule;
            package.members = parser.parseString(SourceCode);

            if (Compiler.errorCount > 0) {
                return;
            }

            package.resolve();

            //StringBuilder sb = new StringBuilder();
            //StringWriter sw = new StringWriter(sb);
            //CCodeGenContext context = new CCodeGenContext(sw);
            //package.emit(context);

            //var textBox = new TextBox();
            //textBox.Multiline = true;
            //textBox.Text = sb.ToString();

            //ShowControl(textBox);

            //File.WriteAllText("__tmp.c", textBox.Text);

            //var processInfo = new System.Diagnostics.ProcessStartInfo(Path.GetFullPath("tinycc/tcc.exe"), Path.GetFullPath("__tmp.c"));
            //processInfo.RedirectStandardError = true;
            //processInfo.RedirectStandardOutput = true;
            //processInfo.UseShellExecute = false;
            //var p = new System.Diagnostics.Process();
            //p.StartInfo = processInfo;
            //p.ErrorDataReceived += (s, ee) => {
            //    Compiler.error(ErrorCode.hexNumberTooLarge, ee.Data);
            //};
            //p.OutputDataReceived += (s, ee) => {
            //    Compiler.echo(ee.Data);
            //};
            //p.Start();
            //p.BeginOutputReadLine();
            //p.BeginErrorReadLine();
            //p.WaitForExit();
            //File.Delete(Path.GetFullPath("__tmp.exe"));
            //System.Diagnostics.Process.Start(Path.GetFullPath("__tmp.exe"));
        }

        private void 生成ToolStripMenuItem_Click(object sender, EventArgs e) {
            SourceCode = @"
extern void system(string command);
extern void printf(char* format, ...);

int main(){
	int i = 0;
	for(i = 0; i < 10; i++) {
		if(i > 1) {
			printf(""hello%d"", i);
		}
	}
	system(""pause"");
	return 0;
}";
        }

        private void samleCodeToolStripMenuItem_Click(object sender, EventArgs e) {

        }

    }

    
}